From da36f65433d3c826221d45d8d46253bb6e24e0b4 Mon Sep 17 00:00:00 2001 From: Happy-melon Date: Fri, 4 Feb 2011 16:39:17 +0000 Subject: [PATCH] Follow-up r64670 (bug22929): cleaner implementation of security for script (and potentially CSS) files. ResourceLoader *already* knows where each module has come from, so all we need to do is filter them in OutputPage according to the desired level of 'trustworthiness'. TODO: * Are there instances where we might want to restrict CSS as well as JS? * Would a $wg config option and/or user preference and/or index.php GET parameter to limit inclusion be useful? * Can we deprecate any of the existing $wg config options? * What's going on with the duplicated code between OutputPage and SkinTemplate? --- includes/OutputPage.php | 145 +++++++++++++----- includes/SkinTemplate.php | 3 + .../resourceloader/ResourceLoaderModule.php | 49 ++++++ .../ResourceLoaderUserModule.php | 1 + .../ResourceLoaderUserOptionsModule.php | 2 + .../ResourceLoaderWikiModule.php | 3 + 6 files changed, 163 insertions(+), 40 deletions(-) diff --git a/includes/OutputPage.php b/includes/OutputPage.php index 77fa0586dc..9cd06ff7bc 100644 --- a/includes/OutputPage.php +++ b/includes/OutputPage.php @@ -124,9 +124,12 @@ class OutputPage { var $mTemplateIds = array(); - /** Initialized with a global value. Let us override it. - * Should probably get deleted / rewritten ... */ - var $mAllowUserJs; + # What level of 'untrustworthiness' is allowed in CSS/JS modules loaded on this page? + # @see ResourceLoaderModule::$origin + # ResourceLoaderModule::ORIGIN_ALL is assumed unless overridden; + protected $mAllowedModules = array( + ResourceLoaderModule::TYPE_COMBINED => ResourceLoaderModule::ORIGIN_ALL, + ); /** * @EasterEgg I just love the name for this self documenting variable. @@ -211,15 +214,6 @@ class OutputPage { 'Cookie' => null ); - /** - * Constructor - * Initialise private variables - */ - function __construct() { - global $wgAllowUserJs; - $this->mAllowUserJs = $wgAllowUserJs; - } - /** * Redirect to $url rather than displaying the normal page * @@ -366,13 +360,34 @@ class OutputPage { return $this->mScripts . $this->getHeadItems(); } + /** + * Filter an array of modules to remove insufficiently trustworthy members + * @param $modules Array + * @return Array + */ + protected function filterModules( $modules, $type = ResourceLoaderModule::TYPE_COMBINED ){ + $resourceLoader = $this->getResourceLoader(); + $filteredModules = array(); + foreach( $modules as $val ){ + $module = $resourceLoader->getModule( $val ); + if( $module->getOrigin() <= $this->getAllowedModules( $type ) ) { + $filteredModules[] = $val; + } + } + return $filteredModules; + } + /** * Get the list of modules to include on this page * + * @param $filter Bool whether to filter out insufficiently trustworthy modules * @return Array of module names */ - public function getModules() { - return array_values( array_unique( $this->mModules ) ); + public function getModules( $filter = false, $param = 'mModules' ) { + $modules = array_values( array_unique( $this->$param ) ); + return $filter + ? $this->filterModules( $modules ) + : $modules; } /** @@ -390,8 +405,8 @@ class OutputPage { * Get the list of module JS to include on this page * @return array of module names */ - public function getModuleScripts() { - return array_values( array_unique( $this->mModuleScripts ) ); + public function getModuleScripts( $filter = false ) { + return $this->getModules( $filter, 'mModuleScripts' ); } /** @@ -410,8 +425,8 @@ class OutputPage { * * @return Array of module names */ - public function getModuleStyles() { - return array_values( array_unique( $this->mModuleStyles ) ); + public function getModuleStyles( $filter = false ) { + return $this->getModules( $filter, 'mModuleStyles' ); } /** @@ -430,8 +445,8 @@ class OutputPage { * * @return Array of module names */ - public function getModuleMessages() { - return array_values( array_unique( $this->mModuleMessages ) ); + public function getModuleMessages( $filter = false ) { + return $this->getModules( $filter, 'mModuleMessages' ); } /** @@ -1065,19 +1080,58 @@ class OutputPage { } /** - * Remove user JavaScript from scripts to load + * Do not allow scripts which can be modified by wiki users to load on this page; + * only allow scripts bundled with, or generated by, the software. */ public function disallowUserJs() { - $this->mAllowUserJs = false; + $this->reduceAllowedModules( + ResourceLoaderModule::TYPE_SCRIPTS, + ResourceLoaderModule::ORIGIN_CORE_INDIVIDUAL + ); } /** * Return whether user JavaScript is allowed for this page - * + * @deprecated @since 1.18 Load modules with ResourceLoader, and origin and + * trustworthiness is identified and enforced automagically. * @return Boolean */ public function isUserJsAllowed() { - return $this->mAllowUserJs; + return $this->getAllowedModules( ResourceLoaderModule::TYPE_SCRIPTS ) >= ResourceLoaderModule::ORIGIN_USER_INDIVIDUAL; + } + + /** + * Show what level of JavaScript / CSS untrustworthiness is allowed on this page + * @see ResourceLoaderModule::$origin + * @param $type String ResourceLoaderModule TYPE_ constant + * @return Int ResourceLoaderModule ORIGIN_ class constant + */ + public function getAllowedModules( $type ){ + if( $type == ResourceLoaderModule::TYPE_COMBINED ){ + return min( array_values( $this->mAllowedModules ) ); + } else { + return isset( $this->mAllowedModules[$type] ) + ? $this->mAllowedModules[$type] + : ResourceLoaderModule::ORIGIN_ALL; + } + } + + /** + * Set the highest level of CSS/JS untrustworthiness allowed + * @param $type String ResourceLoaderModule TYPE_ constant + * @param $level Int ResourceLoaderModule class constant + */ + public function setAllowedModules( $type, $level ){ + $this->mAllowedModules[$type] = $level; + } + + /** + * As for setAllowedModules(), but don't inadvertantly make the page more accessible + * @param $type String + * @param $level Int ResourceLoaderModule class constant + */ + public function reduceAllowedModules( $type, $level ){ + $this->mAllowedModules[$type] = min( $this->getAllowedModules($type), $level ); } /** @@ -2347,7 +2401,7 @@ class OutputPage { * TODO: Document * @param $skin Skin * @param $modules Array/string with the module name - * @param $only string May be styles, messages or scripts + * @param $only String ResourceLoaderModule TYPE_ class constant * @param $useESI boolean * @return string html